package tests;

import static org.junit.Assert.*;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.junit.Test;

import addEmployeeTransaction.AddCommissionedEmployee;
import addEmployeeTransaction.AddHourlyEmployee;
import addEmployeeTransaction.AddSalariedEmployee;
import database.InMemoryPayrollDatabase;
import database.PayrollDatabase;

import main.Paycheck;

import paydayTransaction.PaydayTransaction;
import salesReceiptTransaction.SalesReceiptTransaction;
import timeCardTransaction.TimeCardTransaction;

public class TestPay {

	PayrollDatabase database = InMemoryPayrollDatabase.getInstance();

	@Test
	public void testPaySingleSalariedEmployee() {
		int empId = 1;
		AddSalariedEmployee t = new AddSalariedEmployee(empId, "Bob", "Home",
				1000.00, database);
		t.execute();
		
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		try {
			payDate = format.parse("2001-11-30");
		} catch (ParseException e) {
			fail("parse exception");
		}
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();
		assertNotNull(pt);
	}

	@Test
	public void testPayingSingleHourlyEmployeeNoTimeCards() {
		int empId = 2;
		AddHourlyEmployee t = new AddHourlyEmployee(empId, "Bill", "Home",
				15.25, database);
		t.execute();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		try {
			payDate = format.parse("2001-11-09");
		} catch (ParseException e) {
			fail("parse exception");
		}
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();
		Paycheck pc = pt.getPaycheck(empId);
		assertNotNull(pc);
	}

	@Test
	public void testPaySingleHourlyEmployeeOneTimeCard() {
		int empId = 2;
		AddHourlyEmployee t = new AddHourlyEmployee(empId, "Bill", "Home",
				15.25, database);
		t.execute();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		try {
			payDate = format.parse("2001-11-09");// Friday
		} catch (ParseException e) {
			fail("parse exception");
		}

		TimeCardTransaction tc = new TimeCardTransaction(payDate, 2.0, empId,
				database);
		try {
			tc.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();
		ValidatePaycheck(pt, empId, payDate, 30.5);
	}

	@Test
	public void PaySingleHourlyEmployeeOnWrongDate() {
		int empId = 2;
		AddHourlyEmployee t = new AddHourlyEmployee(empId, "Bill", "Home",
				15.25, database);
		t.execute();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		try {
			payDate = format.parse("2001-11-29");// Thursday
		} catch (ParseException e) {
			fail("parse exception");
		}

		TimeCardTransaction tc = new TimeCardTransaction(payDate, 9.0, empId,
				database);
		try {
			tc.execute();
		} catch (Exception e) {
			fail("exception");
		}
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();

		Paycheck pc = pt.getPaycheck(empId);
		assertNull(pc);
	}

	@Test
	public void PaySingleHourlyEmployeeTwoTimeCards() {
		int empId = 2;
		AddHourlyEmployee t = new AddHourlyEmployee(empId, "Bill", "Home",
				15.25, database);
		t.execute();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		Date payDate2 = null;
		try {
			payDate = format.parse("2001-11-09");// Friday
			payDate2 = format.parse("2001-11-08");// Thursday
		} catch (ParseException e) {
			fail("parse exception");
		}

		TimeCardTransaction tc = new TimeCardTransaction(payDate, 2.0, empId,
				database);
		try {
			tc.execute();
		} catch (Exception e) {
			fail("exception");
		}
		TimeCardTransaction tc2 = new TimeCardTransaction(payDate2, 5.0, empId,
				database);
		try {
			tc2.execute();
		} catch (Exception e) {
			fail("exception");
		}
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();
		ValidatePaycheck(pt, empId, payDate, 7 * 15.25);
	}

	@Test
	public void TestPaySingleHourlyEmployeeWithTimeCardsSpanningTwoPayPeriods() {
		int empId = 2;
		AddHourlyEmployee t = new AddHourlyEmployee(empId, "Bill", "Home",
				15.25, database);
		t.execute();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		Date dateInPreviousPayPeriod = null;
		try {
			payDate = format.parse("2001-11-09"); // Thursday
			dateInPreviousPayPeriod = format.parse("2001-10-30");
		} catch (ParseException e) {
			fail("parse exception");
		}

		TimeCardTransaction tc = new TimeCardTransaction(payDate, 2.0, empId,
				database);
		try {
			tc.execute();
		} catch (Exception e1) {
			fail("exception");
		}
		TimeCardTransaction tc2 = new TimeCardTransaction(
				dateInPreviousPayPeriod, 5.0, empId, database);
		try {
			tc2.execute();
		} catch (Exception e) {
			fail("exception");
		}
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();
		ValidatePaycheck(pt, empId, payDate, 2 * 15.25);
	}

	@Test
	public void PayingSingleCommissionedEmployeeNoReceipts() {
		int empId = 2;
		AddCommissionedEmployee t = new AddCommissionedEmployee(empId, "Bill", "Home", 1500, 10, database);
		t.execute();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		try {
			payDate = format.parse("2001-11-16");// Friday
		} catch (ParseException e) {
			e.printStackTrace();
		}

		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();
		ValidatePaycheck(pt, empId, payDate, 1500.0);
	}

	@Test
	public void PaySingleCommissionedEmployeeOneReceipt() {
		int empId = 2;
		AddCommissionedEmployee t = new AddCommissionedEmployee(empId, "Bill",
				"Home", 1500, 10, database);
		t.execute();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		try {
			payDate = format.parse("2001-11-16");// Friday
		} catch (ParseException e) {
			e.printStackTrace();
		}

		SalesReceiptTransaction sr = new SalesReceiptTransaction(payDate,
				5000.00, empId, database);
		try {
			sr.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();
		ValidatePaycheck(pt, empId, payDate, 2000.00);
	}

	@Test
	public void PaySingleCommissionedEmployeeOnWrongDate() {
		int empId = 2;
		AddCommissionedEmployee t = new AddCommissionedEmployee(empId, "Bill",
				"Home", 1500, 10, database);
		t.execute();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		try {
			payDate = format.parse("2001-11-09");// Friday
		} catch (ParseException e) {
			e.printStackTrace();
		}

		SalesReceiptTransaction sr = new SalesReceiptTransaction(payDate,
				5000.00, empId, database);
		try {
			sr.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		pt.execute();

		Paycheck pc = pt.getPaycheck(empId);
		assertNull(pc);
	}
	
	@Test
	public void PaySingleCommissionedEmployeeTwoReceipts() {
		int empId = 2;
		
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		Date yesterday = null;
		try {
			payDate = format.parse("2001-11-16");
			yesterday = format.parse("2001-11-15");
		} catch (ParseException e1) {
			fail("parse execption");
		}
		
		AddCommissionedEmployee t = new AddCommissionedEmployee(empId, "Bill", "Home", 1500, 10, database);
		SalesReceiptTransaction sr = new SalesReceiptTransaction(payDate, 5000.00, empId, database);
		SalesReceiptTransaction sr2 = new SalesReceiptTransaction(yesterday, 3500.00, empId, database);
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		try {
			t.execute();
			sr.execute();
			sr2.execute();
			pt.execute();
		} catch (Exception e) {
			fail("SalesReceiptTransaction.execute() throws exception");
		}
		assertNotNull(pt.getPaycheck(empId));
		TestPaycheck.validatePaycheck(pt, empId, payDate, 2350.00);
	}

	@Test
	public void TestPaySingleCommissionedEmployeeWithReceiptsSpanningTwoPayPeriods() {
		int empId = 2;
		AddCommissionedEmployee t = new AddCommissionedEmployee(empId, "Bill", "Home", 1500, 10, database);
		t.execute();
		
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date payDate = null;
		Date yesterday = null;
		
		try {
			payDate = format.parse("2001-11-16");
			yesterday = format.parse("2001-11-01");
		} catch (ParseException e1) {
			fail("parse execption");
		}

		SalesReceiptTransaction sr = new SalesReceiptTransaction(payDate, 5000.00, empId, database);
		SalesReceiptTransaction sr2 = new SalesReceiptTransaction(yesterday, 3500.00, empId, database);
		PaydayTransaction pt = new PaydayTransaction(payDate, database);
		try {
			sr.execute();
			sr2.execute();
			pt.execute();
		} catch (Exception e) {
			fail ("Exeption is thrown during execution");
		}
		
		TestPaycheck.validatePaycheck(pt, empId, payDate, 2000.00);
	}

	private void ValidatePaycheck(PaydayTransaction pt, int empid,
			Date payDate, double pay) {
		Paycheck pc = pt.getPaycheck(empid);
		assertNotNull(pc);
		
		System.out.println(payDate + " " + pc.getPayDate());
		
		assertEquals(payDate, pc.getPayDate());
		assertEquals(pay, pc.getGrossPay(), .001);
		assertEquals("Hold", pc.getField("Disposition"));
		assertEquals(0.0, pc.getDeductions(), .001);
		assertEquals(pay, pc.getNetPay(), .001);
	}
}
